package vrp;


/**
 * Class representing a customer to visit.
 * Comparison between two customers is made by comparing their "angle" with the depot on the map
 * @author pmartin
 * 
 */
public class Customer implements Comparable<Customer>{

    /**
     * Customer's name (id)
     */
    private int id=0;

    private Vehicle car;

    private boolean isDepot;

    private int x,y;

    /**
     * Previous customer to visit in the round
     */
    private Customer prevCustomer;

    /**
     * Next customer to visit in the round
     */
    private Customer nextCustomer;

    private int position;

    private int requirement;

    /**
     * @return Returns the name.
     */
    public int getId() {
            return id;
    }
    
    public void setId(int id) {
            if (!isDepot) this.id = id;         // the depot always has id=0;
    }
    /**
     * @return Returns the nextCustomer.
     */
    
    public Customer getNextCustomer() {
            return nextCustomer;
    }

    /**
     * @param nextCustomer
     *            The nextCustomer to set.
     */
    public void setNextCustomer(Customer nextCustomer) {
            if (!isDepot) this.nextCustomer = nextCustomer;     // the depot always has nextCustomer=null
    }

    /**
     * @return Returns the prevCustomer.
     */
    public Customer getPrevCustomer() {
            return prevCustomer;
    }

    /**
     * @param prevCustomer
     *            The prevCustomer to set.
     */
    public void setPrevCustomer(Customer prevCustomer) {
            if (!isDepot) this.prevCustomer = prevCustomer;     // the depot always has prevCustomer=null
    }

    int getReq() {
        if (isDepot && (requirement!=0)) throw new RuntimeException("Requirement of the depot has been changed"); 
        return requirement;
    }
    
    void setReq(int requirement) {
        if (isDepot && (requirement!=0)) throw new RuntimeException("Trying to change the requirment of the depot");
        this.requirement=requirement;
    }

    /**
     * Delete this customer from its current route so that it can be added to another one
     * Link the previous customer with the next on and update vehicle values (length, num customers ...)
     */
    void deleteSelf() {
        if (isDepot) throw new RuntimeException("trying to delete the depot");
        prevCustomer.setNextCustomer(nextCustomer);
        nextCustomer.setPrevCustomer(prevCustomer);
        car.updateUsage(-requirement);
        if (car.getFirstCustomer()==this){
        	car.setFirstCustomer(nextCustomer);
        }
        prevCustomer=null;
        nextCustomer=null;
        car=null;
    }
    
    /**
     * Calculate the direct distance between the previous and the next customer and gives the saved distance.
     * This method permit to simulate the removal/insert of the current customer in a round
     * @param prev Previous customer in the round
     * @param next Next customer in the round
     * @return Distance won by removing the current customer of a round
     */
    double distanceChange(Customer prev, Customer next) {
        return (distanceFrom(prev)+distanceFrom(next)-prev.distanceFrom(next));
    }
    
	/**
	 * Calculate the distance between a given Customer and the current one
	 * Remark : The distance is the shortest one -> the straight line !
	 * @param o Customer from which we have to calculate the distance
	 * @return Calculated distance
	 */
    double distanceFrom(Customer customer) {
       return (Math.sqrt(Math.pow(x-customer.getX(),2)+Math.pow(y-customer.getY(),2))); 
    }
 
    private double getX() {
        return x;
    }

    private double getY() {
        return y;
    }

    void setVehicle(Vehicle vehicle) {
        car=vehicle;
    }    

    int getPosition() {
        return position;
    }

    /**
     * Set a value to the current customer position
     * Increase all the next customers' position
     * @param auxPos
     */
    void setPosition(int auxPos) {
    	if(this.isDepot()) return;
    	
    	Customer nextCustomer = this.getNextCustomer();
        position=auxPos;
        if(!nextCustomer.isDepot()){
        	nextCustomer.setPosition(auxPos+1);
        } else {
        	this.car.setNumCustomers(this.position+1);
        }
        /*while(!nextCustomer.isDepot() && !this.isDepot){
        	nextCustomer.position = auxPos++;
        	nextCustomer = nextCustomer.getNextCustomer();
        }*/
    }

    boolean isDepot() {
        return isDepot;
    }
    
    void setX(int val){
        x = val;
    }
    
    void setY(int val){
        y = val;
    }
    
    void setIsDepot(boolean val){
        isDepot = val;
    }

	/**
	 * Calculate the angle from the problem depot. 
	 * Zero is the top part of the vertical line from the depot
	 * @return Calculated angle
	 */
	public double getAngle(){
		double deltaX = this.getX() - Constants.depot.getX();
		double deltaY = this.getY() - Constants.depot.getY();
		double resu = 0;
		if(deltaY == 0){
			if(deltaX > 0){
				resu = Math.PI / 2;
			} else if (deltaX < 0){
				resu = 3 * Math.PI / 2;
			}
		} else {
			resu = Math.atan(deltaX/deltaY);
		}
		return resu;
	}
		
	@Override
	public int compareTo(Customer o) {
		Double selfAngle = this.getAngle();
		Double otherAngle = o.getAngle();
		int resu = 0;
		if(selfAngle < otherAngle){
			resu = -1; 
		} else if (selfAngle > otherAngle){
			resu = 1;
		}
		return resu;
	}
}
